<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html 
     PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <title>Module: ActiveRecord::Aggregations::ClassMethods</title>
  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
  <meta http-equiv="Content-Script-Type" content="text/javascript" />
  <link rel="stylesheet" href="../../.././rdoc-style.css" type="text/css" media="screen" />
  <script type="text/javascript">
  // <![CDATA[

  function popupCode( url ) {
    window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
  }

  function toggleCode( id ) {
    if ( document.getElementById )
      elem = document.getElementById( id );
    else if ( document.all )
      elem = eval( "document.all." + id );
    else
      return false;

    elemStyle = elem.style;
    
    if ( elemStyle.display != "block" ) {
      elemStyle.display = "block"
    } else {
      elemStyle.display = "none"
    }

    return true;
  }
  
  // Make codeblocks hidden by default
  document.writeln( "<style type=\"text/css\">div.method-source-code { display: none }</style>" )
  
  // ]]>
  </script>

</head>
<body>



    <div id="classHeader">
        <table class="header-table">
        <tr class="top-aligned-row">
          <td><strong>Module</strong></td>
          <td class="class-name-in-header">ActiveRecord::Aggregations::ClassMethods</td>
        </tr>
        <tr class="top-aligned-row">
            <td><strong>In:</strong></td>
            <td>
                <a href="../../../files/vendor/rails/activerecord/lib/active_record/aggregations_rb.html">
                vendor/rails/activerecord/lib/active_record/aggregations.rb
                </a>
        <br />
            </td>
        </tr>

        </table>
    </div>
  <!-- banner header -->

  <div id="bodyContent">



  <div id="contextContent">

    <div id="description">
      <p>
Active Record implements aggregation through a macro-like class method
called <tt><a href="ClassMethods.html#M001344">composed_of</a></tt> for
representing attributes as value objects. It expresses relationships like
&quot;Account [is] composed of Money [among other things]&quot; or
&quot;Person [is] composed of [an] address&quot;. Each call to the macro
adds a description of how the value objects are created from the attributes
of the entity object (when the entity is initialized either as a new object
or from finding an existing object) and how it can be turned back into
attributes (when the entity is saved to the database). Example:
</p>
<pre>
  class Customer &lt; ActiveRecord::Base
    composed_of :balance, :class_name =&gt; &quot;Money&quot;, :mapping =&gt; %w(balance amount)
    composed_of :address, :mapping =&gt; [ %w(address_street street), %w(address_city city) ]
  end
</pre>
<p>
The customer class now has the following methods to manipulate the value
objects:
</p>
<ul>
<li><tt>Customer#balance, Customer#balance=(money)</tt>

</li>
<li><tt>Customer#address, Customer#address=(address)</tt>

</li>
</ul>
<p>
These methods will operate with value objects like the ones described
below:
</p>
<pre>
 class Money
   include Comparable
   attr_reader :amount, :currency
   EXCHANGE_RATES = { &quot;USD_TO_DKK&quot; =&gt; 6 }

   def initialize(amount, currency = &quot;USD&quot;)
     @amount, @currency = amount, currency
   end

   def exchange_to(other_currency)
     exchanged_amount = (amount * EXCHANGE_RATES[&quot;#{currency}_TO_#{other_currency}&quot;]).floor
     Money.new(exchanged_amount, other_currency)
   end

   def ==(other_money)
     amount == other_money.amount &amp;&amp; currency == other_money.currency
   end

   def &lt;=&gt;(other_money)
     if currency == other_money.currency
       amount &lt;=&gt; amount
     else
       amount &lt;=&gt; other_money.exchange_to(currency).amount
     end
   end
 end

 class Address
   attr_reader :street, :city
   def initialize(street, city)
     @street, @city = street, city
   end

   def close_to?(other_address)
     city == other_address.city
   end

   def ==(other_address)
     city == other_address.city &amp;&amp; street == other_address.street
   end
 end
</pre>
<p>
Now it&#8216;s possible to access attributes from the database through the
value objects instead. If you choose to name the composition the same as
the attribute&#8216;s name, it will be the only way to access that
attribute. That&#8216;s the case with our <tt>balance</tt> attribute. You
interact with the value objects just like you would any other attribute,
though:
</p>
<pre>
  customer.balance = Money.new(20)     # sets the Money value object and the attribute
  customer.balance                     # =&gt; Money value object
  customer.balance.exchanged_to(&quot;DKK&quot;) # =&gt; Money.new(120, &quot;DKK&quot;)
  customer.balance &gt; Money.new(10)     # =&gt; true
  customer.balance == Money.new(20)    # =&gt; true
  customer.balance &lt; Money.new(5)      # =&gt; false
</pre>
<p>
Value objects can also be composed of multiple attributes, such as the case
of Address. The order of the mappings will determine the order of the
parameters. Example:
</p>
<pre>
  customer.address_street = &quot;Hyancintvej&quot;
  customer.address_city   = &quot;Copenhagen&quot;
  customer.address        # =&gt; Address.new(&quot;Hyancintvej&quot;, &quot;Copenhagen&quot;)
  customer.address = Address.new(&quot;May Street&quot;, &quot;Chicago&quot;)
  customer.address_street # =&gt; &quot;May Street&quot;
  customer.address_city   # =&gt; &quot;Chicago&quot;
</pre>
<h2>Writing value objects</h2>
<p>
Value objects are immutable and interchangeable objects that represent a
given value, such as a <tt>Money</tt> object representing $5. Two
<tt>Money</tt> objects both representing $5 should be equal (through
methods such as == and &lt;=&gt; from <tt>Comparable</tt> if ranking makes
sense). This is unlike entity objects where equality is determined by
identity. An entity class such as <tt>Customer</tt> can easily have two
different objects that both have an address on Hyancintvej. Entity identity
is determined by object or relational unique identifiers (such as primary
keys). Normal <tt><a href="../Base.html">ActiveRecord::Base</a></tt>
classes are entity objects.
</p>
<p>
It&#8216;s also important to treat the value objects as immutable.
Don&#8216;t allow the <tt>Money</tt> object to have its amount changed
after creation. Create a new <tt>Money</tt> object with the new value
instead. This is exemplified by the <tt>Money#exchanged_to</tt> method that
returns a new value object instead of changing its own values. Active
Record won&#8216;t persist value objects that have been changed through
means other than the writer method.
</p>
<p>
The immutable requirement is enforced by Active Record by freezing any
object assigned as a value object. Attempting to change it afterwards will
result in a <tt>TypeError</tt>.
</p>
<p>
Read more about value objects on <a
href="http://c2.com/cgi/wiki?ValueObject">c2.com/cgi/wiki?ValueObject</a>
and on the dangers of not keeping value objects immutable on <a
href="http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable">c2.com/cgi/wiki?ValueObjectsShouldBeImmutable</a>
</p>

    </div>


   </div>

    <div id="method-list">
      <h3 class="section-bar">Methods</h3>

      <div class="name-list">
      <a href="#M001344">composed_of</a>&nbsp;&nbsp;
      </div>
    </div>

  </div>


    <!-- if includes -->

    <div id="section">





      


    <!-- if method_list -->
    <div id="methods">
      <h3 class="section-bar">Public Instance methods</h3>

      <div id="method-M001344" class="method-detail">
        <a name="M001344"></a>

        <div class="method-heading">
          <a href="#M001344" class="method-signature">
          <span class="method-name">composed_of</span><span class="method-args">(part_id, options = {}, &amp;block)</span>
          </a>
        </div>
      
        <div class="method-description">
          <p>
Adds reader and writer methods for manipulating a value object: <tt><a
href="ClassMethods.html#M001344">composed_of</a> :address</tt> adds
<tt>address</tt> and <tt>address=(new_address)</tt> methods.
</p>
<p>
Options are:
</p>
<ul>
<li><tt>:class_name</tt> - specify the class name of the association. Use it
only if that name can&#8216;t be inferred from the part id. So <tt><a
href="ClassMethods.html#M001344">composed_of</a> :address</tt> will by
default be linked to the <tt>Address</tt> class, but if the real class name
is <tt>CompanyAddress</tt>, you&#8216;ll have to specify it with this
option.

</li>
<li><tt>:mapping</tt> - specifies a number of mapping arrays (attribute,
parameter) that bind an attribute name to a constructor parameter on the
value class.

</li>
<li><tt>:allow_nil</tt> - specifies that the aggregate object will not be
instantiated when all mapped attributes are <tt>nil</tt>. Setting the
aggregate class to <tt>nil</tt> has the effect of writing <tt>nil</tt> to
all mapped attributes. This defaults to <tt>false</tt>.

</li>
</ul>
<p>
An optional block can be passed to convert the argument that is passed to
the writer method into an instance of <tt>:class_name</tt>. The block will
only be called if the argument is not already an instance of
<tt>:class_name</tt>.
</p>
<p>
Option examples:
</p>
<pre>
  composed_of :temperature, :mapping =&gt; %w(reading celsius)
  composed_of(:balance, :class_name =&gt; &quot;Money&quot;, :mapping =&gt; %w(balance amount)) {|balance| balance.to_money }
  composed_of :address, :mapping =&gt; [ %w(address_street street), %w(address_city city) ]
  composed_of :gps_location
  composed_of :gps_location, :allow_nil =&gt; true
</pre>
          <p><a class="source-toggle" href="#"
            onclick="toggleCode('M001344-source');return false;">[Source]</a></p>
          <div class="method-source-code" id="M001344-source">
<pre>
     <span class="ruby-comment cmt"># File vendor/rails/activerecord/lib/active_record/aggregations.rb, line 135</span>
135:       <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">composed_of</span>(<span class="ruby-identifier">part_id</span>, <span class="ruby-identifier">options</span> = {}, <span class="ruby-operator">&amp;</span><span class="ruby-identifier">block</span>)
136:         <span class="ruby-identifier">options</span>.<span class="ruby-identifier">assert_valid_keys</span>(<span class="ruby-identifier">:class_name</span>, <span class="ruby-identifier">:mapping</span>, <span class="ruby-identifier">:allow_nil</span>)
137: 
138:         <span class="ruby-identifier">name</span>        = <span class="ruby-identifier">part_id</span>.<span class="ruby-identifier">id2name</span>
139:         <span class="ruby-identifier">class_name</span>  = <span class="ruby-identifier">options</span>[<span class="ruby-identifier">:class_name</span>] <span class="ruby-operator">||</span> <span class="ruby-identifier">name</span>.<span class="ruby-identifier">camelize</span>
140:         <span class="ruby-identifier">mapping</span>     = <span class="ruby-identifier">options</span>[<span class="ruby-identifier">:mapping</span>]    <span class="ruby-operator">||</span> [ <span class="ruby-identifier">name</span>, <span class="ruby-identifier">name</span> ]
141:         <span class="ruby-identifier">mapping</span>     = [ <span class="ruby-identifier">mapping</span> ] <span class="ruby-keyword kw">unless</span> <span class="ruby-identifier">mapping</span>.<span class="ruby-identifier">first</span>.<span class="ruby-identifier">is_a?</span>(<span class="ruby-constant">Array</span>)
142:         <span class="ruby-identifier">allow_nil</span>   = <span class="ruby-identifier">options</span>[<span class="ruby-identifier">:allow_nil</span>]  <span class="ruby-operator">||</span> <span class="ruby-keyword kw">false</span>
143: 
144:         <span class="ruby-identifier">reader_method</span>(<span class="ruby-identifier">name</span>, <span class="ruby-identifier">class_name</span>, <span class="ruby-identifier">mapping</span>, <span class="ruby-identifier">allow_nil</span>)
145:         <span class="ruby-identifier">writer_method</span>(<span class="ruby-identifier">name</span>, <span class="ruby-identifier">class_name</span>, <span class="ruby-identifier">mapping</span>, <span class="ruby-identifier">allow_nil</span>, <span class="ruby-identifier">block</span>)
146:         
147:         <span class="ruby-identifier">create_reflection</span>(<span class="ruby-identifier">:composed_of</span>, <span class="ruby-identifier">part_id</span>, <span class="ruby-identifier">options</span>, <span class="ruby-keyword kw">self</span>)
148:       <span class="ruby-keyword kw">end</span>
</pre>
          </div>
        </div>
      </div>


    </div>


  </div>


<div id="validator-badges">
  <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
</div>

</body>
</html>